package client

import (
	"github.com/aberic/gnomon"
	"github.com/aberic/gnomon/log"
	proto "gykjgit.dccnet.com.cn/chain/proto/swarm"
	"gykjgit.dccnet.com.cn/chain/swarm/comm"
	"gykjgit.dccnet.com.cn/chain/swarm/model"
	"path/filepath"
	"strconv"
	"strings"
)

type Stack struct {
}

func newStack() *Stack {
	return &Stack{}
}

// Deploy 部署新的堆栈或更新现有堆栈
//
// tail 代表是否实时输出命令执行的日志信息
func (s *Stack) Deploy(compose *model.Compose, stackName string) error {
	log.Info("deploy", log.Server("swarm"))
	yamlFilePath := filepath.Join(comm.WorkPath, stackName, comm.StackYaml)
	if err := compose.WriteTo(yamlFilePath); nil != err {
		return err
	}
	params := []string{"stack", "deploy", "-c", yamlFilePath, stackName}
	_, _, _, err := gnomon.CommandExec("docker", params...)
	return err
}

// List 列出现有堆栈
func (s *Stack) List() (stacks []*proto.Stack, err error) {
	params := []string{"stack", "ls", "--format", "{{.Name}}*#@#*{{.Services}}*#@#*{{.Orchestrator}}"}
	var strArr []string
	if _, _, strArr, err = gnomon.CommandExec("docker", params...); nil != err {
		return nil, err
	}
	for _, str := range strArr {
		if gnomon.StringIsEmpty(str) {
			continue
		}
		fields := strings.Split(str, "*#@#*")
		count, err := strconv.ParseUint(fields[1], 10, 32)
		if nil != err {
			return nil, err
		}
		stacks = append(stacks, &proto.Stack{
			Id:           gnomon.StringTrimN(fields[0]),
			ServiceCount: uint32(count),
			Orchestrator: gnomon.StringTrimN(fields[2]),
		})
	}
	return
}

// Process 列出堆栈中的任务
func (s *Stack) Process(stackName string) (stackProcesses []*proto.StackProcess, err error) {
	params := []string{"stack", "ps", stackName, "--format", "{{.ID}}*#@#*{{.Name}}*#@#*{{.Image}}*#@#*{{.Node}}*#@#*{{.DesiredState}}*#@#*{{.CurrentState}}*#@#*{{.Error}}*#@#*{{.Ports}}"}
	var strArr []string
	if _, _, strArr, err = gnomon.CommandExec("docker", params...); nil != err {
		return nil, err
	}
	for _, str := range strArr {
		if gnomon.StringIsEmpty(str) {
			continue
		}
		fields := strings.Split(str, "*#@#*")
		imageSplit := strings.Split(gnomon.StringTrimN(fields[2]), ":")
		timeState, err := currentStateLastTime(gnomon.StringTrimN(fields[5]))
		if nil != err {
			return nil, err
		}
		stackProcesses = append(stackProcesses, &proto.StackProcess{
			Id:           gnomon.StringTrimN(fields[0]),
			Name:         gnomon.StringTrimN(fields[1]),
			Image:        imageSplit[0],
			ImageVersion: imageSplit[1],
			Node:         gnomon.StringTrimN(fields[3]),
			DesiredState: containerState(gnomon.StringTrimN(fields[4])),
			CurrentState: &proto.Time{
				Sec:  int64(timeState.Second()),
				Nsec: int64(timeState.Nanosecond()),
			},
			ErrMsg: gnomon.StringTrimN(fields[6]),
		})
	}
	return
}

// Services 列出堆栈中的服务
func (s *Stack) Services(stackName string) (services []*proto.StackService, err error) {
	params := []string{"stack", "services", stackName, "--format", "{{.ID}}*#@#*{{.Name}}*#@#*{{.Mode}}*#@#*{{.Replicas}}*#@#*{{.Image}}*#@#*{{.Ports}}"}
	var strArr []string
	if _, _, strArr, err = gnomon.CommandExec("docker", params...); nil != err {
		return nil, err
	}
	for _, str := range strArr {
		if gnomon.StringIsEmpty(str) {
			continue
		}
		fields := strings.Split(str, "*#@#*")
		replicaSplit := strings.Split(gnomon.StringTrimN(fields[3]), "/")
		replicas, err := strconv.ParseUint(replicaSplit[0], 10, 32)
		if nil != err {
			return nil, err
		}
		active, err := strconv.ParseUint(replicaSplit[1], 10, 32)
		if nil != err {
			return nil, err
		}
		imageSplit := strings.Split(gnomon.StringTrimN(fields[4]), ":")
		services = append(services, &proto.StackService{
			Id:           gnomon.StringTrimN(fields[0]),
			Name:         gnomon.StringTrimN(fields[1]),
			Mode:         serviceMode(gnomon.StringTrimN(fields[2])),
			Replicas:     uint32(replicas),
			Active:       uint32(active),
			Image:        imageSplit[0],
			ImageVersion: imageSplit[1],
			Ports:        formatPort(gnomon.StringTrimN(fields[5])),
		})
	}
	return
}

// Remove 删除一个或多个堆栈
func (s *Stack) Remove(stackName ...string) error {
	params := append([]string{"stack", "rm"}, stackName...)
	_, _, _, err := gnomon.CommandExec("docker", params...)
	return err
}
